
/*
** DEFINITIONS SECTION
*/

%token PROGRAM
%token PROCEDURE
%token FUNCTION
%token CONST 
%token TYPE 
%token VAR 
%token ARRAY 
%token BOOLEAN 
%token CHAR 
%token INTEGER
%token REAL 
%token RECORD 
%token BEGIN_ 
%token END 
%token OF 
%token IF 
%token THEN 
%token ELSE 
%token FOR 
%token TO 
%token DOWNTO 
%token DO 
%token NEW 
%token DISPOSE	
%token VIRGULA
%token PONTO_E_VIRGULA 
%token DOIS_PONTOS
%token PONTO 
%token OPERADOR_DE_ATRIBUICAO
%token OPERADOR_DE_SUBFAIXA
%token SOMA 
%token SUBTRACAO
%token ASTERISCO
%token DIVISAO
%token ENDERECO
%token VALOR_DE_PONTEIRO
%token MOD	
%token AND 
%token OR 
%token NOT 
%token IGUALDADE
%token DESIGUALDADE
%token MENOR 
%token MENOR_OU_IGUAL
%token MAIOR 
%token MAIOR_OU_IGUAL
%token ABRE_PARENTESIS 
%token FECHA_PARENTESIS
%token ABRE_COLCHETE
%token FECHA_COLCHETE
%token CONDICIONAL_TERNARIO
%token VALOR_STRING
%token VALOR_CHAR
%token VALOR_INT
%token VALOR_FLOAT
%token VALOR_BOOL
%token IDENTIFICADOR
%token FIM_DE_ARQUIVO	
%token ARROBA
%token WHILE
%token BREAK
%token GOTO
%token READ
%token PRINT
%token REPEAT
%token UNTIL
%token STEP
%token STRING

%start program

%left NOT
%left AND ASTERISCO DIVISAO   
%left SOMA SUBTRACAO MOD OR CONDICIONAL_TERNARIO
%left IGUALDADE DESIGUALDADE MENOR MENOR_OU_IGUAL MAIOR MAIOR_OU_IGUAL

/*
** RULES SECTION
*/
%{
#define YYSTYPE char *

#include <stdio.h>
#include <string.h>

typedef struct{
	int tipo;
	char *conteudo;
} node;

//#define YYSTYPE node*

// struct da tabela de simbolos
union val
{
	int t_int;
	char t_char;
	char* t_string;
	float t_real;
	//bool t_bool;
	
};
struct simbolo
{
	char* nome;
/*
	1- inteiro 2-carac 3-string 4-real 5-bool 6-constante 10-nomedefunção 11-nomedeprocedimento 12-nomedoprograma
*/
	int tipo;
	int escopo; // 0-global  1...-função
	union val valor; // ainda nao sei se há necessidade


};

char * t_tipo;

int ct;

// tabela de simbolos
int aponta_tab = 0;
int rt_tabela = 0;
int t_escopo = 0;
struct simbolo tabela_de_simbolos[1000];

int tipo_herdado, tipo_aux;

FILE* file; 
int label = 1;
	
void create_file(char* nome)
{
	char* outro_nome = strdup(nome);
	strcat(outro_nome, ".c");
	file = fopen(outro_nome, "w");
}

void declaracao_var(char* tipo, char* identificadores)
{
	fprintf(file, "%s %s;\n", tipo, identificadores);
}

char* declara_parametros(char* identificadores, char* tipo)
{
	char* aux = strdup(identificadores);
	char* aux2 = strdup("");
	char* aux3;

	char* teste;
	while(sscanf(aux, "%s", teste) > 0)
	{
		sprintf(aux3, "%s %s ", tipo, teste);
		strcat(aux2, strdup(aux3));
		aux += strlen(teste)+1;
	}
	return aux2;
}

void declara_const()
{
	// Faz nada ainda
}

void declara_tipo() 
{
	// Faz nada ainda
}

void create_func(char* id, char* tipo, char* para, char* declara, char* bloco)
{
	fprintf(file, "%s %s(%s) { %s %s }\n", tipo, id, para, declara, bloco);
}

void create_proc(char* id, char* para)
{
	fprintf(file, "void %s(%s) { }\n", id, para);
}

void create_if(char* expressao, char* statements)
{
	printf("if (!%s) goto label%i", expressao, label);
}

void create_main(char* bloco) 
{
	fprintf(file, "int main(int argc, char** argv) { %s }\n", bloco);
}

%}


%%

program: PROGRAM IDENTIFICADOR { create_file(strdup($2)); } PONTO_E_VIRGULA declarations sub_programs block PONTO {  create_main($7); }
	   ;

declarations: CONST const_declaration
			| TYPE type_declaration
			| VAR var_declaration { $$ = $2; }
			| {$$ = "";}
			;

sub_programs: function_declaration sub_programs
			| procedure_declaration sub_programs
			| { $$ = ""; }
			;

const_declaration:	IDENTIFICADOR OPERADOR_DE_ATRIBUICAO valor PONTO_E_VIRGULA const_declaration
				 |	
				 ;

type_declaration: IDENTIFICADOR DOIS_PONTOS type PONTO_E_VIRGULA type_declaration
				| 
				;

var_declaration: var_declaration id_list DOIS_PONTOS type PONTO_E_VIRGULA { 
							for(;rt_tabela>0;rt_tabela--)
							{


									/*
										1- inteiro 2-carac 3-string 4-real 5-bool 6-constante 10-nomedefunção 11-nomedeprocedimento 12-nomedoprograma
									*/

									if (strcmp($4, "int")==0)
										tabela_de_simbolos[aponta_tab-rt_tabela].tipo = 1;
									else	if (strcmp($4, "float")==0)
										tabela_de_simbolos[aponta_tab-rt_tabela].tipo = 4;
									else	if (strcmp($4, "char*")==0)
										tabela_de_simbolos[aponta_tab-rt_tabela].tipo = 2;


							}
							// reseta contagem de variaveis na lista atual
							rt_tabela=0;													
							// geração de codigo
							declaracao_var(strdup($4), strdup($2)); 
							}
			   | {$$ = ""; }
		       ;

id_list: id_list VIRGULA IDENTIFICADOR { 
	
										// verifica se a variavel ja foi declarada naquele escopo									
										ct=aponta_tab-1;
										int flag=0; // indicador se há uma variavel como mesmo nome
										for(;ct>=0;ct--)
										{

											if (strcmp(tabela_de_simbolos[ct].nome,$3)==0) {flag=1;}
											
										}
										if(flag==1) {printf("\nERRO: Variavel duplicada na linha %d: \"%s\"\nFALHA NA COMPILAÇÃO\n\n",lc,$3); exit(0);}
										else
											{
											// guardando nome da variavel na tabela de simbolos
											tabela_de_simbolos[aponta_tab].escopo = t_escopo;
											tabela_de_simbolos[aponta_tab++].nome = strdup($3);
											rt_tabela++;
											//printf("%s \n",$3); // geração de codigo
			 								sprintf($$, "%s, %s", $1, $3); 
											}
						}
	   | IDENTIFICADOR { 
								

										// verifica se a variavel ja foi declarada naquele escopo									
										ct=aponta_tab-1;
										int flag=0; // indicador se há uma variavel como mesmo nome
										for(;ct>=0;ct--)
										{

											if (strcmp(tabela_de_simbolos[ct].nome,$1)==0) {flag=1;}
											
										}
										if(flag==1) {printf("\nERRO: Variavel duplamente declarada, linha %d: \"%s\"\nFALHA NA COMPILAÇÃO\n\n",lc,$1); exit(0);}
										else
											{

											// guardando nome da variavel na tabela de simbolos
											tabela_de_simbolos[aponta_tab].escopo = t_escopo;
											tabela_de_simbolos[aponta_tab++].nome = strdup($1);
											rt_tabela++;
											// geração de codigo
											$$ = strdup($1); 
											}
						}


	   ;


function_declaration: FUNCTION id ABRE_PARENTESIS parameters FECHA_PARENTESIS DOIS_PONTOS type PONTO_E_VIRGULA declarations block PONTO_E_VIRGULA { create_func($2, $7, $4, $9, $10); } ;

procedure_declaration: PROCEDURE id ABRE_PARENTESIS parameters FECHA_PARENTESIS PONTO_E_VIRGULA declarations block PONTO_E_VIRGULA { create_proc($2, $4); };
	
parameters: parameters PONTO_E_VIRGULA id_list DOIS_PONTOS type { strcat($$, ", "); strcat($$, declara_parametros($3, $5));}
		  | id_list DOIS_PONTOS type { $$ = declara_parametros($1, $3);  }
		  | { $$ = ""; }
		  ;

id: IDENTIFICADOR { $$ = strdup($1);  };

type: INTEGER { $$ = "int"; }
    | BOOLEAN { $$ = "bool"; }
    | REAL { $$ = "float"; }
    | CHAR { $$ = "char*"; }
    | array
    | enum
    | record
    | range
    ;

valor: VALOR_STRING		{ tipo_herdado = 3 ;$$ = $1; }
	 | VALOR_CHAR		{ tipo_herdado = 2 ;$$ = $1; }
	 | VALOR_INT		{ tipo_herdado = 1 ;$$ = $1; }
	 | VALOR_FLOAT		{ tipo_herdado = 4 ;$$ = $1; }
	 | VALOR_BOOL		{ tipo_herdado = 5 ;$$ = $1; }
	 ;

array:	ARRAY range OF type
	 ;

enum: ABRE_PARENTESIS subrange FECHA_PARENTESIS
	;

range: ABRE_COLCHETE list_subrange FECHA_COLCHETE
	 ;

list_subrange: list_subrange VIRGULA subrange
			 | subrange
			 ;

subrange: regra_x OPERADOR_DE_SUBFAIXA regra_x
		;

regra_x: access
	   | valor
	   ;

record: RECORD id_list DOIS_PONTOS type END
	  ;

block: BEGIN_ statements END { $$ = $2; }
	 ;

statements: statement PONTO_E_VIRGULA statements { sprintf($$, "%s %s", $1, $3); }
		  | { $$ = ";";}
		  ;

statement: block { $$ = $1; }
		 | expression { $$ = $1; }
		 | control { $$ = $1; }
		 | BREAK { $$ = "break"; }
		 | access OPERADOR_DE_ATRIBUICAO expression { 
														ct=aponta_tab-1;
														 // procurando variavel na tabela de simbolos
														int flag=0;
														for(;ct>=0;ct--)
														{
															
															if (strcmp(tabela_de_simbolos[ct].nome,$1)==0 && t_escopo==tabela_de_simbolos[ct].escopo) {
																flag=1;
																tipo_aux = tabela_de_simbolos[ct].tipo;
																}
											
														}

														if (flag){
															if (tipo_herdado==tipo_aux)
																{
																// geração de codigo
																sprintf($$, "%s = %s", $1, $3); 
																}
															else	
																{
																printf("\nERRO: Atribuição de tipos incompatíveis na linha %d: \"%s\" e \"%s\"\nFALHA NA COMPILAÇÃO\n\n",lc,$1,$3); exit(0);
																}
															}
															else
															{printf("\nERRO: Variavel não declarada na linha %d: \"%s\"\nFALHA NA COMPILAÇÃO\n\n",lc,$1); exit(0);}
													}
		 ;

access: id { 
						ct=aponta_tab-1;
						// procurando id na tabela de simbolos
						int flag=0;						
						for(;ct>=0;ct--)
										{
											if (strcmp(tabela_de_simbolos[ct].nome,$1)==0 && t_escopo==tabela_de_simbolos[ct].escopo) 
												{
												tipo_herdado = tabela_de_simbolos[ct].tipo;
												flag = 1;
												break;
												}
										}
					// a variavel nao foi encontrada na tabela de simbolos	
					if (flag==0) {printf("\nERRO: Variavel não declarada na linha %d: \"%s\"\nFALHA NA COMPILAÇÃO\n\n",lc,$1); exit(0);}
											



				}
	  | access ABRE_COLCHETE expression FECHA_COLCHETE
	  | access PONTO IDENTIFICADOR
	  ;

expression: expression oper1 factor
		  | expression CONDICIONAL_TERNARIO factor DOIS_PONTOS factor
		  | factor { $$ = $1; }
		  ;

oper1: SOMA { $$ = $1; }
	 | SUBTRACAO { $$ = $1; }
	 | MENOR { $$ = $1; }
	 | MAIOR { $$ = $1; }
	 | MENOR_OU_IGUAL { $$ = $1; }
	 | MAIOR_OU_IGUAL { $$ = $1; }
	 | IGUALDADE { $$ = "=="; }
	 | DESIGUALDADE { $$ = "!="; }
	 | OR { $$ = $1; }
	 | ASTERISCO { $$ = $1; }
	 | DIVISAO { $$ = $1; }
	 | AND { $$ = $1; }
	 ;

factor: access 
	  | NOT expression { sprintf($$, "!%s", $2); }
	  | SOMA expression { sprintf($$, "+ %s", $2); }
	  | SUBTRACAO expression { sprintf($$, "- %s", $2); }
	  | ABRE_PARENTESIS expression FECHA_PARENTESIS { sprintf($$, "( %s )", $2); }
	  | valor { $$ = $1; }
	  ;

control: IF expression THEN statements { create_if($1, $3); }
	   | IF expression THEN statements  ELSE statements 
	   | WHILE expression DO statements 
	   | REPEAT statements UNTIL expression 
	   | FOR expression TO expression STEP expression DO block 
	   ;

%%

/*
** CODE SECTION
*/

#include "lex.yy.c"



struct simbolo tabela[100];
int posicao_na_tabela = 0;


int yyerror(char *message) {
	mensagemErro(message, lc);
	return 1;
}

int main(int argc, char** argv) {

	yyin = fopen(argv[1], "r");
	yyparse( );

	printf("tabela de simbolos\n");	
	for (ct=0;ct<aponta_tab;ct++)
	{
		printf("Simbolo-> %s\t Tipo->%d\t Escopo->%d\n",tabela_de_simbolos[ct].nome,tabela_de_simbolos[ct].tipo,tabela_de_simbolos[ct].escopo);	
			
	}


	if (ha_erro==0) // para erros sintaticos
	printf("\n\nPROGRAMA COMPILADO COM SUCESSO!\n\n");	
	else
	mostrarMensagens( );
	return EXIT_SUCCESS;
}
